#include "lexicalanalyser.h"

#pragma region Symbol Class

qint32 Symbol::_lexical_err_cnt;		// deklaracja zmiennej statyczniej, z dedykacja dla linkera!

Symbol::Symbol()
	: _type(UnknownSym)
{
}

Symbol::Symbol(SymbolType type, QString value)
	: _value(value)
	, _type(type)
{	
}

Symbol::Symbol(SymbolType type, QString value, QString err)
	: _value(value)
	, _type(type)
{
	error(err);
}

Symbol::~Symbol()
{
}

void Symbol::error(QString err)
{
	_value.prepend( QObject::tr("ERR: ") );
	_value.append(  QObject::tr(" ") +  err + QObject::tr(" expected") );

	++_lexical_err_cnt;
}

SymbolType Symbol::type()
{
	return _type;
}

QString Symbol::value()
{
	return _value;
}

qint32 Symbol::err_cnt()
{
	return Symbol::_lexical_err_cnt;
}

void Symbol::reset()
{
	Symbol::_lexical_err_cnt = 0;
}

#pragma endregion

#pragma region LexicalAnalyser Class

LexicalAnalyser::LexicalAnalyser(QObject *parent)
	: QObject(parent)
{
	_code = new SourceManager(this);
}

LexicalAnalyser::LexicalAnalyser(QString &code, QObject *parent)
	: QObject(parent)	
{
	_code = new SourceManager(code, this);
	curr_char = _code->nextChar();
}

LexicalAnalyser::~LexicalAnalyser()
{

}

Symbol LexicalAnalyser::nextSymbol()
{
	QString s;			// symbol tekstowo

	// koniec pliku, wiecej symboli nie bedzie
	if(curr_char == 0)
	{
		return Symbol(NoMoreSymbols, s);
	}		

	// pomin znaki biale
	while(isWhiteChar(curr_char))
	{
		curr_char =  _code->nextChar();
		if(curr_char == 0)
		{
			return Symbol(NoMoreSymbols, s);
		}	
	}

	#pragma region identyfikator, stala logiczna lub slowo kluczowe

	if(isAlpha(curr_char))
	{
		while(isAlphaNum(curr_char))
		{
			s.append(curr_char);
			curr_char = _code->nextChar();
			if(curr_char == 0)
			{
				return Symbol(NoMoreSymbols, s, tr("char/digit"));
			}
		}

		SymbolType sym = isKeyWord(s);

		if(sym != UnknownSym)
			return Symbol(sym, s);
		else
			return Symbol(IdSym, s);		
	}

	#pragma endregion

	#pragma region fraction: integer ( epsilon | 'i' integer '|' integer | '|' integer )

	if(isDigit(curr_char))
	{
		// integer
		while(isDigit(curr_char))
		{
			s.append(curr_char);
			curr_char = _code->nextChar();			
		}

		// 'i'
		if(curr_char == 'i')
		{
			s.append(curr_char);
			curr_char = _code->nextChar();

			if(curr_char == 0)
			{
				return Symbol(NoMoreSymbols, s, tr("digit"));
			}

			// integer
			if(isDigit(curr_char))
			{
				while(isDigit(curr_char))
				{
					s.append(curr_char);
					curr_char = _code->nextChar();					
				}
				
				if(curr_char == 0)
				{
					return Symbol(NoMoreSymbols, s, tr("|"));
				}

				// '|'
				if(curr_char == '|')
				{
					s.append(curr_char);
					curr_char = _code->nextChar();
					
					if(curr_char == 0)
					{
						return Symbol(NoMoreSymbols, s, tr("digit"));
					}

					// integer
					if(isDigit(curr_char))
					{
						while(isDigit(curr_char))
						{
							s.append(curr_char);
							curr_char = _code->nextChar();							
						}
					}
					else
					{
						return Symbol(UnknownSym, s, tr("digit"));
					}
				}
				else
				{					
					return Symbol(UnknownSym, s, tr("|"));
				}
			}
			else
			{
				return Symbol(UnknownSym, s, tr("digit"));
			}
		}
		if(curr_char == '|')
		{
			s.append(curr_char);
			curr_char = _code->nextChar();

			if(curr_char == 0)
			{
				return Symbol(NoMoreSymbols, s, tr("digit"));
			}

			// integer
			if(isDigit(curr_char))
			{
				while(isDigit(curr_char))
				{
					s.append(curr_char);
					curr_char = _code->nextChar();
				}
			}
			else
			{
				return Symbol(UnknownSym, s, tr("digit"));
			}
		}

		return Symbol(FractionConstSym, s);
	}

	#pragma endregion

	#pragma region reszta ( ';', ':', '"', '*', '+', '-', '/', '>=', ':=' etc )

	char c = curr_char;
	s.append(curr_char);	

	curr_char = _code->nextChar();

	switch(c)
	{	
		case ';':
			return Symbol(SemiColonSym, s);
		case ':':
			if(curr_char == '=')
			{
				s.append(curr_char);
				curr_char = _code->nextChar();
				return Symbol(AssigmentSym, s);
			}
			return Symbol(ColonSym, s);
		case '.':
			return Symbol(DotSym, s);
		case ',':
			return Symbol(ComaSym, s);
		case '(':
			return Symbol(LBracketSym, s);
		case ')':
			return Symbol(RBracketSym, s);
		case '+':
			return Symbol(PlusSym, s);
		case '-':
			return Symbol(MinusSym, s);
		case '<':
			if(curr_char == '=')
			{
				s.append(curr_char);
				curr_char = _code->nextChar();
				return Symbol(LessEqualSym, s);
			}
			return Symbol(LessSym, s);
		case '>':
			if(curr_char == '=')
			{
				s.append(curr_char);
				curr_char = _code->nextChar();
				return Symbol(GreaterEqualSym, s);
			}
			return Symbol(GreaterSym, s);
		case '=':
			return Symbol(EqualSym, s);
		case '!':
			if(curr_char == '=')
			{
				s.append(curr_char);
				curr_char = _code->nextChar();
				return Symbol(NotEqualSym, s);
			}
			else
				return Symbol(UnknownSym, s, tr("="));
		case '*':
			return Symbol(MultilpySym, s);
		case '/':
			return Symbol(DivideSym, s);
		case '"':
			while(curr_char != '"')
			{
				s.append(curr_char);
				curr_char = _code->nextChar();
				if(curr_char == 0)
				{
					QString r = s.mid(1, s.size() - 2);
					return Symbol(NoMoreSymbols, r, tr("\""));
				}
			}
			s.append(curr_char);
			curr_char = _code->nextChar();
			return Symbol(TextSym, s.mid(1, s.size() - 2));
		default:
			return Symbol(UnknownSym, s, tr("???"));
	}

	#pragma endregion

	
}

void LexicalAnalyser::setSource(QString &source)
{
	_code->setSource(source);
	curr_char = _code->nextChar();
	//test();
}

qint32 LexicalAnalyser::line()
{
	return (_code->position()).row();
}

void LexicalAnalyser::test()
{
	Symbol tmp = nextSymbol();

	while(tmp.type() != NoMoreSymbols)
	{
		qDebug() << "SYM: " << tmp.value();
		tmp = nextSymbol();
	}
}

#pragma region private methods

void LexicalAnalyser::skipWhiteChars()
{
	
}

bool LexicalAnalyser::isAlpha(char c)
{
	return isalpha(c);
}

bool LexicalAnalyser::isAlphaNum(char c)
{
	return isalnum(c);
}

bool LexicalAnalyser::isDigit(char c)
{
	return isdigit(c);
}

bool LexicalAnalyser::isWhiteChar(char c)
{
	return isspace(c);
}

SymbolType LexicalAnalyser::isKeyWord(QString s)
{	
	if(s == tr("Program"))	
		return ProgramSym;	
	else if(s[0] == 'p')
	{
		if(s == tr("procedure"))
			return ProcedureSym;
		else if(s == tr("print"))
			return PrintVarCallSym;
		else 
			return UnknownSym;
	}
	else if(s == tr("begin"))	
		return BeginSym;	
	else if(s == tr("return"))	
		return ReturnSym;	
	else if(s[0] == 'e')
	{
		if(s == tr("end"))
			return EndSym;
		else if(s == tr("else"))
			return ElseSym;
		else if(s == tr("endif"))
			return EndIfSym;
		else if(s == tr("endwhile"))
			return EndWhileSym;
		else 
			return UnknownSym;
	}
	else if(s == tr("not"))	
		return NotSym;	
	else if(s == tr("or"))	
		return OrSym;	
	else if(s == tr("and"))	
		return AndSym;	
	else if(s == tr("getFraction"))	
		return GetVarCallSym;		
	else if(s == tr("if"))	
		return IfSym;	
	else if(s == tr("then"))	
		return ThenSym;		
	else if(s == tr("while"))	
		return WhileSym;	
	else if(s == tr("do"))	
		return DoSym;	
	else if(s == tr("true") || s == tr("false"))
		return BoolConstSym;		 
	else if(s == tr("fraction") || s == tr("void") || s == tr("bool"))
		return TypeSym;		 		
	else if(s == tr("var"))
		return VariableSym;	
	else if(s == tr("def"))
		return DefSym;	
	else if(s == tr("main"))
		return MainSym;	
	else	
		return UnknownSym;
	
}

#pragma endregion

#pragma endregion